/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.connection;

import com.hercules.android.receiver.LogUtil;
import com.hercules.android.receiver.connection.parser.MessageParser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * <p>
 * Represents the base server connection class that holds some common fields of the concrete ServerConnection
 * implementations.
 * </p>
 * <p>
 * AndroidAssembly1 moved to base package, add implemented interface
 * <p>
 * <strong> Thread safety:</strong> Not thread safe since it is mutable.
 * </p>
 *
 * @author DanLazar, morehappiness
 * @author 7realm
 * @version 1.0
 * @param <T> the type of session information
 */
public abstract class BaseServerConnection<T> implements ServerConnection {
    /**
     * Represents the logger used to perform logging.
     * <p>
     * Will be set at the point of definition and never changed. Will be used by subclasses to perform logging. Cannot
     * be null.
     * <p>
     * AndroidAssembly1 changed logger name
     */
    private final Logger logger;

    /**
     * Represents the number of reconnect attempts.
     * <p>
     * Can be set using the setter. It has a getter. Used in TCPConnectionHandler.run and HTTPConnectionHandler.run
     * method. Must be >= 0. Default is 2.
     */
    private int reconnectAttempts = 2;

    /**
     * Represents the interval between reconnect attempts in milliseconds.
     * <p>
     * Can be set using the setter. It has a getter. Used in TCPConnectionHandler.run and HTTPConnectionHandler.run
     * methods. Must be > 0. Time unit: milliseconds. Default is 100 milliseconds.
     */
    private int reconnectAttemptsInterval = 100;

    /**
     * Represents the interval before reconnecting attempts in milliseconds.
     * <p>
     * Can be set using the setter. It has a getter. Used in TCPConnectionHandler.run and HTTPConnectionHandler.run
     * methods. Must be > 0. Time unit: milliseconds. Default is 100 milliseconds.
     */
    private int reconnectWaitInterval = 100;

    /**
     * Represents the number of current reconnect attempts.
     * <p>
     * Can be set using the setter. It has a getter. Used in TCPConnectionHandler.run and HTTPConnectionHandler.run
     * method. Must be >= 0.
     * <p>
     */
    private int currentReconnectAttempts = 0;

    /**
     * This list contains session guids for which the connection to the server was closed.
     * <p>
     * Will be set set in constructor to an empty list. Will be used in TCPConnectionHandler.run() and
     * HTTPConnectionHandler.run() and close() methods. Cannot contain null/empty elements. The reference is final but
     * the contents can be modified in TCPConnectionHandler.run(), HTTPConnectionHandler.run() and close() methods.
     */
    private final List<String> closedConnections;

    /** Represents the map of sessions. */
    private final Map<String, T> sessions;

    /**
     * Represents the parser user to parse commands and encode events.
     * <p>
     * Can be set using the setter. It has a getter. Used in TCPConnectionHandler.run() and HTTPConnectionHandler.run()
     * methods. Cannot be null.
     */
    private MessageParser parser;

    /**
     * <p>
     * Represents the exception handler used in the started threads to handle exceptions.
     * </p>
     * <p>
     * Can be set using the setter. It has a getter.
     * </p>
     * <p>
     * Used in TCPConnectionHandler.run() and HTTPConnectionHandler.run() methods. Cannot be null.
     * </p>
     */
    private ConnectionErrorHandler exceptionHandler;

    /**
     * Creates a new instance.
     */
    protected BaseServerConnection() {
        logger = LogUtil.getLogger(getClass().getSimpleName());
        closedConnections = Collections.synchronizedList(new ArrayList<String>());
        sessions = Collections.synchronizedMap(new HashMap<String, T>());
    }

    /**
     * Gets the map of sessions.
     *
     * @return the map of sessions
     */
    protected Map<String, T> getSessions() {
        return sessions;
    }

    /**
     * Gets the number of current reconnect attempts.
     *
     * @return the number of current reconnect attempts
     */
    protected int getCurrentReconnectAttempts() {
        return currentReconnectAttempts;
    }

    /**
     * Sets the number of current reconnect attempts to zero.
     */
    protected void resetCurrentReconnectAttempts() {
        currentReconnectAttempts = 0;
    }

    /**
     * Increases the number of current reconnect attempts.
     */
    protected void increaseCurrentReconnectAttempts() {
        currentReconnectAttempts++;
    }

    /**
     * Gets value of logger field.
     *
     * @return the value of logger field
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * Getter for the reconnectAttempts field.
     *
     * @return the reconnectAttempts field value
     */
    public int getReconnectAttempts() {
        return reconnectAttempts;
    }

    /**
     * Setter for the reconnectAttempts field.
     *
     * @param reconnectAttempts the reconnectAttempts field value to set
     * @throws IllegalArgumentException if reconnectAttempts < 0
     */
    public void setReconnectAttempts(int reconnectAttempts) {
        ConnectionUtil.checkInt(reconnectAttempts, 0, "reconnectAttempts");
        this.reconnectAttempts = reconnectAttempts;
    }

    /**
     * Getter for the reconnectAttemptsInterval field.
     *
     * @return the reconnectAttemptsInterval field value
     */
    public int getReconnectAttemptsInterval() {
        return reconnectAttemptsInterval;
    }

    /**
     * Setter for the reconnectAttemptsInterval field.
     *
     * @param reconnectAttemptsInterval the reconnectAttemptsInterval field value to set
     * @throws IllegalArgumentException if reconnectAttemptsInterval <= 0
     */
    public void setReconnectAttemptsInterval(int reconnectAttemptsInterval) {
        ConnectionUtil.checkInt(reconnectAttemptsInterval, 1, "reconnectAttemptsInterval");
        this.reconnectAttemptsInterval = reconnectAttemptsInterval;
    }

    /**
     * Getter for the reconnectWaitInterval field.
     *
     * @return the reconnectWaitInterval field value
     */
    public int getReconnectWaitInterval() {
        return reconnectWaitInterval;
    }

    /**
     * Setter for the reconnectWaitInterval field.
     *
     * @param reconnectWaitInterval the reconnectWaitInterval field value to set
     * @throws IllegalArgumentException if reconnectWaitInterval <= 0
     */
    public void setReconnectWaitInterval(int reconnectWaitInterval) {
        ConnectionUtil.checkInt(reconnectWaitInterval, 1, "reconnectWaitInterval");
        this.reconnectWaitInterval = reconnectWaitInterval;
    }

    /**
     * Getter for the closedConnections field.
     *
     * @return the closedConnections field
     */
    public List<String> getClosedConnections() {
        return closedConnections;
    }

    /**
     * Getter for the parser field.
     *
     * @return the parser field value
     */
    public MessageParser getParser() {
        return parser;
    }

    /**
     * Setter for the parser field.
     *
     * @param parser the parser field value to set
     * @throws IllegalArgumentException if parser is null
     */
    public void setParser(MessageParser parser) {
        ConnectionUtil.checkNull(null, parser, "parser", null);
        this.parser = parser;
    }

    /**
     * Getter for the exceptionHandler field.
     *
     * @return the exceptionHandler field value
     */
    public ConnectionErrorHandler getExceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Setter for the exceptionHandler field.
     *
     * @param exceptionHandler the exceptionHandler field value to set
     * @throws IllegalArgumentException if exceptionHandler is null
     */
    public void setExceptionHandler(ConnectionErrorHandler exceptionHandler) {
        ConnectionUtil.checkNull(null, exceptionHandler, "exceptionHandler", null);
        this.exceptionHandler = exceptionHandler;
    }
}
